package com.sz.app.oss.exception;

import com.google.common.collect.Lists;
import com.sz.common.base.dto.ResultDto;
import com.sz.common.base.exception.BizException;
import com.sz.common.base.exception.Exceptions;
import com.sz.common.base.i18n.I18nMessages;
import com.sz.common.base.logger.Logger;

import com.sz.common.base.utils.BeanUtils;
import com.sz.common.base.utils.SerializeUtil;
import com.sz.common.core.system.service.SysMailService;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.UnauthenticatedException;
import org.apache.shiro.authz.UnauthorizedException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.multipart.MultipartException;

import javax.servlet.http.HttpServletRequest;
import java.util.List;

/**
 * Function: GlobalExceptionHandler <br>
 * Author: wentao.chang <br>
 * Date: 2016-10-25 14:11:00
 */
@RestControllerAdvice
public class GlobalExceptionHandler {

    Logger logger = Logger.getLogger(this.getClass());

    @Autowired
    protected I18nMessages messageSource;

    @Autowired
    private SysMailService sysMailService;

    private static List<String> emailReceives = Lists.newArrayList();

    static {
        emailReceives.add("changwenwen@126.com");
    }

    @ExceptionHandler(value = BizException.class)
    public ResultDto defaultErrorHandler(HttpServletRequest req, BizException exception)
            throws Exception {
        exception.printStackTrace();
        return getExceptionDto(exception);
    }

    @ExceptionHandler(value = MultipartException.class)
    public ResultDto multipartExceptionHandler(MultipartException exception) throws Exception {
        logger.error(SerializeUtil.logExceptionStack(exception));
        //100018文件太大
        return getExceptionDto(Exceptions.bizException(ErrorCodes.ERROR_FILE_IS_TOO_BIG));
    }

    @ExceptionHandler(value = MissingServletRequestParameterException.class)
    public ResultDto missingServletRequestParameterExceptionHandler(MissingServletRequestParameterException exception) throws Exception {
        logger.error(SerializeUtil.logExceptionStack(exception));
        return getExceptionDto(Exceptions.bizException(ErrorCodes.ERROR_ARGUMENT_EMPTY, exception.getParameterName()));
    }


    @ExceptionHandler(value = AuthorizationException.class)
    public ResultDto authorizationExceptionHandler(AuthorizationException exception)
            throws Exception {
        logger.warn(SerializeUtil.logExceptionStack(exception));
        //401会话失效或没有登录
        return getExceptionDto(Exceptions.bizException(ErrorCodes.ERROR_401_MSG));
    }

    @ExceptionHandler(value = UnauthenticatedException.class)
    public ResultDto unauthenticatedExceptionHandler(UnauthenticatedException exception)
            throws Exception {
        logger.warn(SerializeUtil.logExceptionStack(exception));
        //401会话失效或没有登录
        return getExceptionDto(Exceptions.bizException(ErrorCodes.ERROR_401_MSG));
    }

    @ExceptionHandler(value = UnauthorizedException.class)
    public ResultDto unauthorizedExceptionHandler(HttpServletRequest req, UnauthorizedException exception)
            throws Exception {

        logger.warn(SerializeUtil.logExceptionStack(exception));
        String queryString = StringUtils.hasLength(req.getQueryString()) ? ("?" + req.getQueryString()) : "";
        String errorString = req.getMethod() + " " + req.getRequestURI() + queryString;
        //403接口api未授权
        return getExceptionDto(Exceptions.bizException(ErrorCodes.ERROR_403_MSG, errorString));

    }

    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public ResultDto methodArgumentNotValidExceptionHandler(MethodArgumentNotValidException exception) throws Exception {
        logger.error(SerializeUtil.logExceptionStack(exception));


        BindingResult bindingResult = exception.getBindingResult();
        if (bindingResult.hasErrors()) {
            List<ObjectError> list = bindingResult.getAllErrors();
            for (ObjectError error : list) {
                String message = messageSource.getMessage(error.getDefaultMessage(), error.getArguments());
                return new ResultDto(-1, message);
            }
        }
        return new ResultDto(-1, "");
    }

    @ExceptionHandler(value = Exception.class)
    public ResultDto defaultErrorHandler(HttpServletRequest req, Exception exception) throws Exception {
        logger.error(SerializeUtil.logExceptionStack(exception));
        //check if need to show details information for client
        //just show on dev
        boolean needShowDetail = "dev".equals(BeanUtils.getActiveProfiles()[0]);

        String queryString = StringUtils.hasLength(req.getQueryString()) ? ("?" + req.getQueryString()) : "";
        String errorString = req.getMethod() + " " + req.getRequestURI() + queryString + "\r\n";
        if (needShowDetail) {
            errorString += exception.getMessage();
        }
        String errStrace = StringUtils.arrayToDelimitedString(exception.getStackTrace(), "\r\n");
        //发送邮件
        try {
            boolean needSendMail = !needShowDetail;
            if (needSendMail) {
                sysMailService.createMail("test@3zang.com", emailReceives.toArray(new String[]{}),
                        BeanUtils.getActiveProfiles()[0] + "系统500异常", errorString + "\r\n" + errStrace);
            }
        } catch (Exception e) {
            logger.error("------无能为力");
        }
        //500服务器内部错误
        return getExceptionDto(Exceptions.bizException(ErrorCodes.ERROR_500_MSG, errorString));
    }


    private ResultDto getExceptionDto(BizException exception) {
        ResultDto dto = new ResultDto();
        dto.setErrorCode(exception.getErrorCode());
        dto.setMsg(exception.getMsg());
        if (exception.getData() != null) {
            dto.setData(exception.getData());
        }
        return dto;
    }


}
